#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#if __APPLE__ && __MACH__
#include <OpenGL/gl.h>   /* for Mac OS X */
#include <OpenGL/glu.h>   /* for Mac OS X */
#ifndef HAVE_NO_GLUT
#include <GLUT/glut.h>   /* for Mac OS X */
#endif
#else
#include <GL/gl.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "prof.h"
#include "prof_internal.h"

#pragma warning(disable:4305; disable:4244; disable:4996)

/* use factor to compute a glow amount */
static int get_colors(int id, float factor,
                       float text_color_ret[3],
                       float glow_color_ret[3],
                       float *glow_alpha_ret)
{
   const float GLOW_RANGE = 0.5f;
   const float GLOW_ALPHA_MAX = 0.5f;
   float glow_alpha;
   int i;
#if 0	
   float hot[3] = {1, 1.0, 0.9};
   float cold[3] = {0.15, 0.9, 0.15};
   float glow_cold[3] = {0.5f, 0.5f, 0};
   float glow_hot[3] = {1.0f, 1.0f, 0};
#endif
   float fr, fg, fb;
   float id_color[3];

   if (id == 0)
   {
   }
   else {
	   int r,g,b;
	   id = (id >> 8) + id;
	   r = id * 37;
	   g = id * 59;
	   b = id * 45;
	   r = (r & 127) + 80;
	   g = (g & 127) + 80;
	   b = (b & 127) + 80;
	   fr = r; fr /= 255.0f;
	   fg = g; fg /= 255.0f;
	   fb = b; fb /= 255.0f;

	   id_color[0] = fr;
	   id_color[1] = fg;
	   id_color[2] = fb;

	   text_color_ret[0] = fr * 0.8f;
	   text_color_ret[1] = fg * 0.8f;
	   text_color_ret[2] = fb * 0.8f;
   }

   if (factor < 0) factor = 0;
   if (factor > 1) factor = 1;

   for (i=0; i < 3; ++i)
	   text_color_ret[i] = id_color[i] * 0.7f + id_color[i] * ( 0.2f * factor );

   /* Figure out whether to start up the glow as well. */
   glow_alpha = (factor - GLOW_RANGE) / (1 - GLOW_RANGE);
   if (glow_alpha < 0) {
      *glow_alpha_ret = 0;
      return 0;
   }

   for (i=0; i < 3; ++i)
      glow_color_ret[i] = id_color[i] * 0.8f + id_color[i] * ( 0.2f * factor );

   *glow_alpha_ret = glow_alpha * GLOW_ALPHA_MAX;
   return 1;
}

static void draw_rectangle(float x0, float y0, float x1, float y1)
{
   /* FACE_CULL is disabled so winding doesn't matter */
   glVertex2f(x0, y0);
   glVertex2f(x1, y0);
   glVertex2f(x1, y1);
   glVertex2f(x0, y1);
}

typedef struct
{
   float x0,y0;
   float sx,sy;
} GraphLocation;

static void graph_func(int id, int x0, int x1, float *values, void *data)
{
   GraphLocation *loc = (GraphLocation *) data;
   int i, r,g,b;

   /* trim out values that are under 0.2 ms to accelerate rendering */
   while (x0 < x1 && (*values < 0.0002f)) { ++x0; ++values; }
   while (x1 > x0 && (values[x1-1-x0] < 0.0002f)) --x1;

   if (id == 0)
      glColor4f(1,1,1,0.5);
   else {
      if (x0 == x1) return;

      id = (id >> 8) + id;
      r = id * 37;
      g = id * 59;
      b = id * 45;
#pragma warning(disable:4761)
      glColor3ub((r & 127) + 80, (g & 127) + 80, (b & 127) + 80);
   }

   glBegin(GL_LINE_STRIP);
   if (x0 == x1) {
      float x,y;
      x = loc->x0 + x0 * loc->sx;
      y = loc->y0 + values[0] * loc->sy;
      glVertex2f(x,loc->y0);
      glVertex2f(x, y);
   }
   for (i=0; i < x1-x0; ++i) {
      float x,y;
      x = loc->x0 + (i+x0) * loc->sx;
      y = loc->y0 + values[i] * loc->sy;
      glVertex2f(x,y);
   }
   glEnd();
}

Prof_extern_C void Prof_draw_graph_gl(float sx, float sy, float x_spacing, float y_spacing)
{
#ifdef Prof_ENABLED
   Prof_Begin(iprof_draw_graph)
   GraphLocation loc = { sx, sy, x_spacing, y_spacing * 1000 };
   Prof_graph(128, graph_func, &loc);
   Prof_End
#endif
}


// float to string conversion with sprintf() was
// taking up 10-20% of the Prof_draw time, so I
// wrote a faster float-to-string converter

static char int_to_string[100][4];
static char int_to_string_decimal[100][4];
static char int_to_string_mid_decimal[100][4];
static void int_to_string_init(void)
{
   int i;
   for (i=0; i < 100; ++i) {
      sprintf(int_to_string[i], "%d", i);
      sprintf(int_to_string_decimal[i], ".%02d", i);
      sprintf(int_to_string_mid_decimal[i], "%d.%d", i/10, i % 10);
   }
}

static char *formats[5] =
{
   "%.0f",
   "%.1f",
   "%.2f",
   "%.3f",
   "%.4f",
};

static void float_to_string(char *buf, float num, int precision)
{
   int x,y;
   switch(precision) {
      case 2:
         if (num < 0 || num >= 100)
            break;
         x = num;
         y = (num - x) * 100;
         strcpy(buf, int_to_string[x]);
         strcat(buf, int_to_string_decimal[y]);
         return;
      case 3:
         if (num < 0 || num >= 10)
            break;
         num *= 10;
         x = num;
         y = (num - x) * 100;
         strcpy(buf, int_to_string_mid_decimal[x]);
         strcat(buf, int_to_string_decimal[y]+1);
         return;
      case 4:
         if (num < 0 || num >= 1)
            break;
         num *= 100;
         x = num;
         y = (num - x) * 100;
         buf[0] = '0';
         strcpy(buf+1, int_to_string_decimal[x]);
         strcat(buf, int_to_string_decimal[y]+1);
         return;
   }
   sprintf(buf, formats[precision], num);
}

void Prof_draw_gl_background(Prof_Report *pob,
							 float sx, float sy,
							 float full_width, float height,
							 float line_spacing, int precision,
							 void (*printText)(float x, float y, char *str), float (*textWidth)(char *str));


void Prof_draw_gl_background(Prof_Report *pob,
							 float sx, float sy,
				  float full_width, float height,
				  float line_spacing, int precision,
				  void (*printText)(float x, float y, char *str), float (*textWidth)(char *str))
{
#ifdef Prof_ENABLED
	Prof_Begin(iprof_draw_background)

	int i,n,o;
	GLuint cull, texture;
	float backup_sy;

	float field_width = textWidth("5555.55");
	float name_width  = full_width - field_width * 3;
	float plus_width  = textWidth("+");

	int max_records;


	if (!int_to_string[0][0]) int_to_string_init();

	if (precision < 1) precision = 1;
	if (precision > 4) precision = 4;

	// disable face culling to avoid having to get winding correct
	texture = glIsEnabled(GL_TEXTURE_2D);
	cull = glIsEnabled(GL_CULL_FACE);
	if (cull == GL_TRUE) {
		glDisable(GL_CULL_FACE);
	}


	for (i=0; i < NUM_TITLE; ++i) {
		if (pob->title[i]) {
			float header_x0 = sx;
			float header_x1 = header_x0 + full_width;

			if (i == 0)
				glColor4f(0.1f, 0.3f, 0, 0.85);
			else
				glColor4f(0.2f, 0.1f, 0.1f, 0.85);

			glBegin(GL_QUADS);
			draw_rectangle(header_x0, sy-2, header_x1, sy-line_spacing+2);
			glEnd();

			sy += 1.5*line_spacing;
			height -= abs(line_spacing)*1.5;
		}
	}

	max_records = height / abs(line_spacing);

	o = 0;
	n = pob->num_record;
	if (n > max_records) n = max_records;
	if (pob->hilight >= o + n) {
		o = pob->hilight - n + 1;
	}

	backup_sy = sy;

	// Draw the background colors for the zone data.
	glDisable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);

	glColor4f(0,0,0,0.85);
	draw_rectangle(sx, sy, sx + full_width, sy - line_spacing);
	sy += line_spacing;

	for (i = 0; i < n; i++) {
		float y0, y1;

		if (i & 1) {
//			glColor4f(0.1, 0.1f, 0.2, 0.85);
			glColor4f(0.1, 0.1f, 0.1, 0.85);
		} else {
//			glColor4f(0.1f, 0.1f, 0.3, 0.85);
			glColor4f(0.2f, 0.2f, 0.2, 0.85);
		}
		if (i+o == pob->hilight)
//			glColor4f(0.3f, 0.3f, 0.1f, 0.85);
			glColor4f(0.3f, 0.3f, 0.3f, 0.85);

		y0 = sy;
		y1 = sy - line_spacing;

		draw_rectangle(sx, y0, sx + full_width, y1);
		sy += line_spacing;
	}
	glEnd();

	if (cull == GL_TRUE)
		glEnable(GL_CULL_FACE);
	if (texture == GL_TRUE)
		glEnable(GL_TEXTURE_2D);

	Prof_End
#endif
}

void Prof_draw_gl_text(Prof_Report *pob,
					   float sx, float sy,
					   float full_width, float height,
					   float line_spacing, int precision,
					   void (*printText)(float x, float y, char *str), float (*textWidth)(char *str));

void Prof_draw_gl_text(Prof_Report *pob,
					   float sx, float sy,
				  float full_width, float height,
				  float line_spacing, int precision,
				  void (*printText)(float x, float y, char *str), float (*textWidth)(char *str))
{
#ifdef Prof_ENABLED
	Prof_Begin(iprof_draw_text)

	int i,j,n,o;
	GLuint cull, texture;

	float field_width = textWidth("5555.55");
	float name_width  = full_width - field_width * 3;
	float plus_width  = textWidth("+");

	int max_records;

	if (!int_to_string[0][0]) int_to_string_init();

	if (precision < 1) precision = 1;
	if (precision > 4) precision = 4;

	// disable face culling to avoid having to get winding correct
	texture = glIsEnabled(GL_TEXTURE_2D);
	cull = glIsEnabled(GL_CULL_FACE);
	if (cull == GL_TRUE) {
		glDisable(GL_CULL_FACE);
	}

	pob = Prof_create_report();

	for (i=0; i < NUM_TITLE; ++i) {
		if (pob->title[i]) {
			if (i == 0) 
				glColor4f(0.6, 0.4, 0, 0.8);
			else
				glColor4f(0.8f, 0.1f, 0.1f, 0.8);

			printText(sx+2, sy, pob->title[i]);

			sy += 1.5*line_spacing;
			height -= abs(line_spacing)*1.5;
		}
	}

	max_records = height / abs(line_spacing);

	o = 0;
	n = pob->num_record;
	if (n > max_records) n = max_records;
	if (pob->hilight >= o + n) {
		o = pob->hilight - n + 1;
	}

	glColor4f(0.7,0.7,0.7,0);

	if (pob->header[0])
		printText(sx+8, sy, pob->header[0]);

	for (j=1; j < NUM_HEADER; ++j)
		if (pob->header[j])
			printText(sx + name_width + field_width * (j-1) + 
			field_width/2 - textWidth(pob->header[j])/2, sy, pob->header[j]);

	sy += line_spacing;

	for (i = 0; i < n; i++) {
		char buf[256], *b = buf;
		Prof_Report_Record *r = &pob->record[i+o];
		float text_color[3], glow_color[3];
		float glow_alpha;
		float x = sx + textWidth(" ") * r->indent + plus_width/2;
		int id = (int)(r->zone);
		if (r->prefix) {
			buf[0] = r->prefix;
			++b;
		} else {
			x += plus_width;
		}
		if (r->number)
			sprintf(b, "%s (%d)", r->name, r->number);
		else
			sprintf(b, "%s", r->name);
		if (get_colors(id, r->heat, text_color, glow_color, &glow_alpha)) {
			glColor4f(glow_color[0], glow_color[1], glow_color[2], glow_alpha);
			printText(x+2, sy-1, buf);
		}
		glColor3fv(text_color);
		printText(x + 1, sy, buf);

		for (j=0; j < NUM_VALUES; ++j) {
			if (r->value_flag & (1 << j)) {
				int pad;
				float_to_string(buf, r->values[j], j == 2 ? 2 : precision);
				pad = field_width- plus_width - textWidth(buf);
				if (r->indent) pad += plus_width;
				printText(sx + pad + name_width + field_width * j, sy, buf);
			}
		}


		sy += line_spacing;
	}

	if (cull == GL_TRUE)
		glEnable(GL_CULL_FACE);
	if (texture == GL_TRUE)
		glEnable(GL_TEXTURE_2D);

	Prof_End
#endif
}




Prof_extern_C void Prof_draw_gl(float sx, float sy,
                                float full_width, float height,
                                float line_spacing, int precision,
    void (*printText)(float x, float y, char *str), float (*textWidth)(char *str))
{
#ifdef Prof_ENABLED
   Prof_Begin(iprof_draw)


   Prof_Report *pob;


   pob = Prof_create_report();

   Prof_draw_gl_text(pob, sx, sy, full_width, height, line_spacing, precision , printText, textWidth );
   Prof_draw_gl_background(pob, sx, sy, full_width, height, line_spacing, precision , printText, textWidth );

   Prof_free_report(pob);


   Prof_End
#endif
}
